package wsi.exp.exp3;

import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.rygh.core.html.Html;
import org.rygh.core.html.element.HtmlElement;
import org.rygh.core.html.element.HtmlParagraph;
import org.rygh.core.html.table.HtmlTable;
import org.rygh.core.html.table.TableColumnStyle;
import org.rygh.core.html.table.TableData;
import org.rygh.core.html.table.TableTuple;
import org.rygh.core.util.UIo;
import org.rygh.core.util.UTime;
import org.rygh.core.xml.UXml;
import org.rygh.core.xml.XmlLeaf;
import org.rygh.core.xml.XmlObject;
import org.rygh.core.xml.XmlPath;
import org.rygh.core.xml.XmlTree;

import wsi.exp.Exp2TreeToEntity;
import wsi.exp.ExpFolderController;
import wsi.exp.ExpXml.Tag;
import wsi.exp.exp3.compare.Exp3Comparator;
import wsi.exp.exp3.compare.Exp3PairComparator;
import wsi.exp.exp3.compare.Exp3PredComparator;
import wsi.function.Function;
import wsi.match.MatchProperty;
import wsi.script.AppConfig;

public abstract class Exp3 extends Exp2TreeToEntity {

	protected final Map<String, Exp3Comparator> comparatorMap = new HashMap<>();

	private final Map<Function, GoldSetMatch> goldSetMap3 = new HashMap<>();
	private final Map<Function, PathMatch> automaticMap3 = new HashMap<>();

	private final Exp3Tex tex;
	private final String type;

	protected Set<Function> functionSet;

	protected Exp3(String type) {
		this.type = type;
		this.tex = new Exp3Tex(type);

		tex.setComparatorMap(comparatorMap);
	}

	@SuppressWarnings("deprecation")
	private void _load3(File folder, Map<Function, PathMatch> map, boolean xml) {
		PathMatch match;

		for (File file : folder.listFiles()) {
			if (!file.isFile())
				continue;

			// System.out.println("_load3 " + file.getName());

			String fname = UIo.getFileNameWithoutExtension(file.getName());
			Function function = parseFileName(fname);

			String content = UIo.readFile(file);

			match = new PathMatch();

			if (xml)
				match.loadXml(content);
			else
				match.load(content);

			assert match != null;
			map.put(function, match);
		}

	}

	@SuppressWarnings("deprecation")
	private void _loadGold(File folder, Map<Function, GoldSetMatch> map,
			boolean xml) {
		GoldSetMatch match;

		for (File file : folder.listFiles(UXml.XML_FILTER)) {
			if (!file.isFile())
				continue;

			String fname = UIo.getFileNameWithoutExtension(file.getName());

			Function function = parseFileName(fname);

			String content = UIo.readFile(file);
			match = new GoldSetMatch();

			if (xml)
				match.loadXml(content);
			else
				match.load(content);

			assert match != null;
			map.put(function, match);
		}
	}

	private void appendData(TableTuple tuple, Function function,
			String methodName) {
		for (int t = 0; t < 100; t += 10) {
			String key = Exp3Comparator.getName(function, t);

			Exp3Comparator comparator = comparatorMap.get(key);
			assert comparator != null : String.format("%d, %s", t,
					function.getFullName());

			try {
				Method method = comparator.getClass().getMethod(methodName);
				Double d = (Double) method.invoke(comparator);

				tuple.add(String.format(String.format("%.2f", d)));
			} catch (Exception e) {

			}
		}
	}

	public XmlTree getXmlTree(XmlPath rootPath, MatchProperty matchProperty,
			PathMatch pathMatch, boolean includeCount) {
		XmlTree xml = new XmlTree();
		XmlLeaf parent, child;
		XmlObject obj;

		obj = new XmlObject(Tag.result.name());
		obj.addAttribute(Tag.experiment.name(), "3");
		obj.addAttribute(Tag.description.name(),
				"XmlPath to Predicate Path Match");
		obj.addAttribute(Tag.date.name(), UTime.toTodayString());

		parent = new XmlLeaf(xml.getRoot(), obj);

		obj = new XmlObject(Tag.subtree_root.name(), rootPath.stripIndex());

		new XmlLeaf(parent, obj);

		obj = new XmlObject(Tag.match.name());
		obj.addAttribute(Tag.type.name(), matchProperty.getType().name());

		switch (matchProperty.getType()) {
		case EXISTS:
			obj.setValue("1");
			break;

		case FORALL:
			obj.setValue(matchProperty.getPredicatePath().getKey());
			break;
		}

		new XmlLeaf(parent, obj);

		obj = new XmlObject(Tag.matches.name());
		parent = new XmlLeaf(parent, obj);

		for (PathMatchPair pair : pathMatch.getPairs()) {
			obj = new XmlObject(Tag.match.name());

			if (includeCount) {
				obj.addAttribute(Tag.count.name(),
						Integer.toString(pair.getCount()));
				obj.addAttribute(Tag.total.name(),
						Integer.toString(pair.getTotal()));
			}

			child = new XmlLeaf(parent, obj);

			if (includeCount) {
				obj = new XmlObject(Tag.percentage.name());
				obj.setValue(Double.toString(pair.getConfidence()));
				new XmlLeaf(child, obj);
			}

			obj = new XmlObject(Tag.predicate_path.name());
			obj.setValue(pair.getPredicatePath().getKey());
			new XmlLeaf(child, obj);

			XmlPath xpath = pair.getXmlPath();
			XmlPath relpath = xpath.getRelativePathsTo(rootPath);

			obj = new XmlObject(Tag.xpath.name());
			obj.setValue(relpath == null ? xpath.stripIndex() : relpath
					.stripIndex());
			new XmlLeaf(child, obj);
		}

		return xml;
	}

	@Deprecated
	public void load3(File folder, Map<Function, PathMatch> map) {
		_load3(folder, map, false);
	}

	@Deprecated
	public void loadGold(File folder, Map<Function, GoldSetMatch> map) {
		_loadGold(folder, map, false);
	}

	public void loadXml3(File folder, Map<Function, PathMatch> map) {
		_load3(folder, map, true);
	}

	public void loadXmlGold(File folder, Map<Function, GoldSetMatch> map) {
		_loadGold(folder, map, true);
	}

	@Override
	public void compute() {
		super.compute();

		ExpFolderController folder = ExpFolderController.shared();

		// loadGold(folder.getGoldFolderForExp3Debug(), goldSetMap);
		// load3(folder.getFolderForExp3Debug(), automaticMap);

		loadXmlGold(folder.getGoldFolderForExp3(), goldSetMap3);
		loadXml3(folder.getFolderForExp3(), automaticMap3);

		// only count relations with at least one match.

		functionSet = new HashSet<>(automaticMap3.keySet());
		tex.setFunctionSet(functionSet);

		Set<Function> toremove = new HashSet<>();
		List<HtmlElement> elements = new ArrayList<>();

		Exp3Comparator comparator = null;

		for (Function function : functionSet) {
			if (!goldSetMap3.containsKey(function)) {
				System.out.printf("[warn] gold set 3 does not contain %s.\n",
						function.getFullName());
				toremove.add(function);
				continue;
			}

			if (!exp2ResultMap.containsKey(function)
					|| !exp2ResultMap.get(function)) {
				toremove.add(function);
				continue;
			}

			XmlPath xpath = goldSetMap1.get(function);
			List<MatchProperty> matches = goldSetMap2.get(function);

			String str = "";

			for (MatchProperty i : matches)
				str += i.toString() + "; ";

			elements.clear();
			elements.add(new HtmlParagraph(String.format(
					"<b>Isomorphic Subtree Root</b>: %s", xpath.stripIndex())));
			elements.add(new HtmlParagraph(String.format(
					"<b>Tree to Entity Match</b>: %s", str)));

			GoldSetMatch goldSet = goldSetMap3.get(function);
			PathMatch result = automaticMap3.get(function);

			for (int t = 90; t >= 0; t -= 10) {

				switch (type) {
				case "pair":
					comparator = new Exp3PairComparator(function, t);
					break;

				case "pred":
					comparator = new Exp3PredComparator(function, t);
					break;

				default:
					assert false;
					break;
				}

				comparator.setContextInfo(elements);
				comparator.setGoldSet(goldSet);
				comparator.setMatchResult(result);
				comparator.calibrate();
				comparatorMap.put(comparator.getName(), comparator);
			}
		}

		functionSet.removeAll(toremove);
	}

	@Override
	public String save() {
		tex.save();
		return saveHtml();
	}

	private String saveHtml() {
		Html html = new Html("Experiment 3 - By " + type);
		html.addElement(AppConfig.shared().getConfigHtml());

		HtmlTable table;
		Exp3Comparator comparator = null;
		List<HtmlElement> elements = new ArrayList<>();

		//

		int t;

		TableData data;
		TableTuple tuple;

		// gather by precision
		data = new TableData();
		tuple = new TableTuple();
		tuple.add("Web Service", "Function");

		for (t = 0; t < 100; t += 10)
			tuple.add(String.format("t = %d%%", t));

		for (t = 0; t < 100; t += 10)
			tuple.add(String.format("t = %d%%", t));

		for (t = 0; t < 100; t += 10)
			tuple.add(String.format("t = %d%%", t));

		for (t = 0; t < 100; t += 10)
			tuple.add("Hit", "Total");

		for (t = 0; t < 100; t += 10)
			tuple.add(String.format("t = %d%%", t));

		tuple.add("Gold");

		data.add(tuple);

		for (Function function : functionSet) {
			tuple = new TableTuple();
			tuple.add(function.getService());
			tuple.add(function.getName());

			appendData(tuple, function, "getPrecision");
			appendData(tuple, function, "getAveragePrecision");
			appendData(tuple, function, "getFScore");

			// hit/total
			for (t = 0; t < 100; t += 10) {
				String key = Exp3Comparator.getName(function, t);

				comparator = comparatorMap.get(key);

				if (t == 0)
					elements.addAll(comparator.getHtmlElements());

				tuple.add(String.format(String.format("%s%d</a>",
						Exp3Comparator.getAnchor(function, 0),
						comparator.getRelevant())));

				tuple.add(String.format(String.format("%d",
						comparator.getRetrieved())));
			}

			appendData(tuple, function, "getRecall");

			tuple.add(String.format("%d", comparator.getGoldSize()));

			data.add(tuple);
		}

		data.sort();

		table = new HtmlTable("Experiment 3, Path Match <b>(Precision)</b> - "
				+ type, data);

		StringBuilder sbuilder = new StringBuilder();

		for (t = 0; t < 100; t += 10)
			sbuilder.append(String.format("<th colspan=2>t = %d%%</th>", t));

		table.setHeaderPrefix("<tr><th rowspan=2 colspan=3>&nbsp;</th>"
				+ "<th rowspan=2 colspan=10>Precision</th>"
				+ "<th rowspan=2 colspan=10>Average Precision</th>"
				+ "<th rowspan=2 colspan=10>F-Score</th>"
				+ "<th colspan=20>Hit / Total</th>"
				+ "<th rowspan=2 colspan=10>Recall</th>"
				+ "<th rowspan=2>&nbsp;</th></tr>" + "<tr>"
				+ sbuilder.toString() + "</tr>");

		TableColumnStyle colStyle = table.getColumnStyle();

		int b = 3, l = 10;
		for (int i = b; i < b + l; i++)
			colStyle.set(i, Color.red);

		b += l;
		for (int i = b; i < b + l; i++)
			colStyle.set(i, Color.blue);

		b += l;
		for (int i = b; i < b + l; i++)
			colStyle.set(i, Color.orange);

		b += l;
		for (int i = b; i < b + 2 * l; i++)
			colStyle.set(i, Color.green);

		b += 2 * l;
		for (int i = b; i < b + l; i++)
			colStyle.set(i, Color.blue);

		html.addElement(table);

		//

		appendMissingHtml(html, goldSetMap3.keySet(), automaticMap3.keySet());

		for (HtmlElement e : elements)
			html.addElement(e);

		//

		String htmlstr = html.toString();

		ExpFolderController folder = ExpFolderController.shared();
		UIo.saveFile(
				new File(folder.getResultFolder(), String.format(
						"%s3_result_by_%s.html", ExpFolderController.EXP, type)),
				htmlstr);

		return htmlstr;
	}

	@Deprecated
	protected String toHtmlString(String type, int old) {
		Html html = new Html("Experiment 3 - " + type);
		HtmlTable table;
		Exp3Comparator comparator;
		int t;

		TableData data;
		TableTuple tuple;

		// gather by precision
		data = new TableData();
		tuple = new TableTuple();
		tuple.add("Web Service", "Function");

		for (t = 0; t < 100; t += 10)
			tuple.add(String.format("t = %.2f", .01 * t));

		data.add(tuple);

		for (Function function : functionSet) {
			tuple = new TableTuple();
			tuple.add(function.getService());
			tuple.add(function.getName());

			for (t = 0; t < 100; t += 10) {
				String key = Exp3Comparator.getName(function, t);

				comparator = comparatorMap.get(key);
				assert comparator != null : String.format("%d, %s", t,
						function.getFullName());

				tuple.add(String.format(String.format("%s%.2f</a>",
						Exp3Comparator.getAnchor(function, t),
						comparator.getPrecision())));
			}

			data.add(tuple);
		}

		data.sort();

		table = new HtmlTable("Experiment 3, Path Match <b>(Precision)</b> - "
				+ type, data);
		html.addElement(table);
		html.newhr();

		// gather by recall
		data = new TableData();
		tuple = new TableTuple();
		tuple.add("Web Service", "Function");

		for (t = 0; t < 100; t += 10)
			tuple.add(String.format("t = %.2f", .01 * t));

		data.add(tuple);

		for (Function function : functionSet) {
			tuple = new TableTuple();
			tuple.add(function.getService());
			tuple.add(function.getName());

			for (t = 0; t < 100; t += 10) {
				String key = Exp3Comparator.getName(function, t);

				comparator = comparatorMap.get(key);
				assert comparator != null : String.format("%d, %s", t,
						function.getFullName());

				tuple.add(String.format(String.format("%s%.2f</a>",
						Exp3Comparator.getAnchor(function, t),
						comparator.getRecall())));
			}

			data.add(tuple);
		}

		data.sort();

		table = new HtmlTable("Experiment 3, Path Match <b>(Recall)</b> - "
				+ type, data);
		html.addElement(table);
		html.newhr();

		// normal

		data = new TableData();
		tuple = new TableTuple();
		tuple.add("Web Service", "Function");

		for (t = 0; t < 100; t += 10)
			tuple.add("Gold Size", "Threshold", "Precision", "Recall", "Hit",
					"Total");

		data.add(tuple);

		//

		List<HtmlElement> elements = new ArrayList<>();

		for (Function function : functionSet) {
			tuple = new TableTuple();
			tuple.add(function.getService());
			tuple.add(function.getName());

			for (t = 0; t < 100; t += 10) {
				String key = Exp3Comparator.getName(function, t);

				comparator = comparatorMap.get(key);
				assert comparator != null : String.format("%d, %s", t,
						function.getFullName());

				elements.addAll(comparator.getHtmlElements());

				tuple.add(String.format("%d", comparator.getGoldSize()));
				tuple.add(String.format("%s%.2f</a>",
						Exp3Comparator.getAnchor(function, t), .01 * t));

				tuple.add(String.format("%.2f", comparator.getPrecision()));
				tuple.add(String.format("%.2f", comparator.getRecall()));
				tuple.add(String.format("%d", comparator.getRelevant()));
				tuple.add(String.format("%d", comparator.getRetrieved()));
			}

			data.add(tuple);
		}

		data.sort();

		table = new HtmlTable("Experiment 3, Path Match - " + type, data);
		html.addElement(table);

		for (HtmlElement e : elements)
			html.addElement(e);

		//

		String htmlstr = html.toString();

		ExpFolderController folder = ExpFolderController.shared();
		UIo.saveFile(
				new File(folder.getResultFolder(), String.format(
						"%s3_result_by_%s.html", ExpFolderController.EXP, type)),
				htmlstr);

		return htmlstr;
	}
}
